#include <iostream>
#include "InetAddr.hpp"
#include <cstring>
#include <memory>
#include <cerrno>
#include <pthread.h>
#include "log.hpp"
#include <sys/wait.h>
#include <sys/types.h>
#include <functional>
using namespace LogMudule;
using namespace Inet;
#define BACKLOG 8
const static int gdefaultlistenfd = -1;
const static int gdefaultport = 8888;
using handler_t = std::function<std::string(std::string&)>;
class TcpServer
{
  struct PthClass
  {
    int sockfd;
    TcpServer * self;  
  };
public:
  TcpServer(handler_t handler,uint16_t port = gdefaultport, int listenfd = gdefaultlistenfd) : _port(port), 
  _listenfd(listenfd),
  _isrunning(false),
  _handler(handler)
  {
  }
  ~TcpServer()
  {
  }
  void Init()
  {
    _listenfd = ::socket(AF_INET, SOCK_STREAM, 0);
    if (_listenfd < 0)
    {
      LOG(LogLevel::FATAL) << "socket:" << strerror(errno);
      Die(SOCKET_ERR);
    }
    LOG(LogLevel::INFO) << "socket success"<<_listenfd;
    InetAdrr addr(_port);
    int n = ::bind(_listenfd, addr.NetAddr(), addr.NetAddrLen());
    if (n < 0)
    {
      LOG(LogLevel::FATAL) << "bind:" << strerror(errno);
      Die(BIND_ERR);
    }
    LOG(LogLevel::INFO) << "bind success "<<_listenfd;
    int m = ::listen(_listenfd,BACKLOG);
    if(m<0)
    {
      LOG(LogLevel::WARNING)<<"listen fail";
      Die(LISTEN_ERR);
    }
    LOG(LogLevel::INFO) << "listen success "<<_listenfd;

  }
  //声明为类外函数才没有this指针
  static void * ThreadEntry(void * args)
  {
    //该线程结束后会自动清理资源 且不用被等待
    pthread_detach(pthread_self());
    PthClass * pthc = (PthClass*)args;
    pthc->self->handrequest(pthc->sockfd);
    return nullptr;
  }
  void handrequest(int sockfd)
  {
    while(true)
    {
        char buff[4096];
        ssize_t n = ::read(sockfd,buff,sizeof(buff)-1);
        std::string pacakge;
        if(n>0)
        {
          LOG(LogLevel::INFO) <<buff;
          buff[n]=0;
          pacakge+=buff; 
          std::string echo_string = _handler(pacakge);
          if(echo_string.empty())
            continue;
          ::send(sockfd,echo_string.c_str(),echo_string.size(),0);
        }
        else if(n==0)
        {
          LOG(LogLevel::DEBUG)<<"客户端退出"<<sockfd;
          break;
        }
        else
        {
          //读取失败
          break;
        }
    }
    //防止资源泄露
    ::close(sockfd);
  }


  void Start()
  {
    _isrunning=true;
    while(_isrunning)
    {
      struct sockaddr_in peer;
      socklen_t len = sizeof(peer);
      //这里的sockfd就是客户端那边的sockfd 通过tcp三次握手建立连接
      int sockfd = ::accept(_listenfd,CONV(&peer),&len);
      if(sockfd<0)
      {
        LOG(LogLevel::INFO) << "accept fali ";
      }
      InetAdrr addr(peer); 
      LOG(LogLevel::DEBUG)<<addr.IP()<<":"<<addr.Port();
      //version_0 单进程版本
      //  handrequest(sockfd);
      //-----------------------------------------------
      //version_1 多进程版本
      // pid_t id = fork();
      // if(id==0)
      // {
      //   //子进程只用直接执行handrequest不需要用到listenfd 所以关闭listenfd以免fd泄露
      //   ::close(_listenfd);
      //   //父进程等待子进程退出的话 会阻塞到单个子进程的handrequest的while(true)循环里 因此两种方法 1.忽略子进程退出发出的回收信号 
      //   //让子进程变成孤儿进程由系统回收::signal(SIGCHLD, SIG_IGN); 2.创建孙子进程 关闭儿子进程 孙子进程执行handrequest并由系统回收 
      //   if(fork()>0)
      //   {
      //     //儿子进程退出
      //     exit(0);
      //   }
      //   //孙子进程执行handrequest
      //   handrequest(sockfd);
      //   exit(0);
      // }
      // //父进程用不到sockfd了 关闭
      // ::close(sockfd);
      // //这里等待第二次fork后退出的子进程 不回收会出现资源泄露问题
      // pid_t rid= waitpid(id,nullptr,0);
      // if(rid<0)
      // {
      //   LOG(LogLevel::WARNING)<<"wait error";
      // }
      //-----------------------------------------------------------------
      //version-2 多线程版本
      PthClass *pthc = new PthClass;
      pthread_t pth;
      pthc->self =this;
      pthc->sockfd=sockfd;
      pthread_create(&pth,nullptr,ThreadEntry,pthc);

    }
    
  }
  void Stop()
  {
    _isrunning=false;
  }

private:
  // 类比张三在外揽客 监听需要连接的socket
  int _listenfd;
  uint16_t _port;
  bool _isrunning;
  handler_t _handler;
};